package code.google.jcontainer.support.intercept;

import code.google.jcontainer.resolver.MethodAnnotationResolver;
import code.google.jcontainer.resolver.MethodResolveContext;
import code.google.jcontainer.resolver.ResolverException;
import org.apache.commons.logging.LogFactory;

import java.lang.reflect.Method;

/**
 * &#64;Intercept resolver, call beforeMethod & afterMethod in the component at BEFORE_METHOD and AFTER_METHOD
 *
 * @author <a href="mailto:jfox.young@gmail.com">Young Yang</a>
 */
public class InterceptMethodAnnotationResolver implements MethodAnnotationResolver{

    private static final org.apache.commons.logging.Log log = LogFactory.getLog(InterceptMethodAnnotationResolver.class);

    public void resolveMethodAnnotationBeforeConstruct(MethodResolveContext methodResolveContext) throws ResolverException {

    }

    public void resolveMethodAnnotationAfterConstruct(MethodResolveContext methodResolveContext) throws ResolverException {
        
    }

    public void resolveMethodAnnotationBeforeMethod(final MethodResolveContext methodResolveContext) throws ResolverException {
        Object component = methodResolveContext.getComponent();
        Method method = methodResolveContext.getMethod();
        if(!(component instanceof Interceptable)) {
            throw new ResolverException("Component " + methodResolveContext.getComponent() + " has @Intercept on Method: " + method.getName() + ", but doesn't implement " + Interceptable.class.getName());
        }

        try {
            ((Interceptable) component).beforeMethod(new InterceptContext() {

                public Object getTarget() {
                    return methodResolveContext.getComponent();
                }

                public Method getMethod() {
                    return methodResolveContext.getMethod();
                }

                public Object[] getParameters() {
                    return methodResolveContext.getParameters();
                }

                public void setParameters(Object[] objects) {
                    methodResolveContext.setParameters(objects);
                }

                public void setResult(Object result) {
                    throw new InterceptException("Not support InterceptContext.setResult in beforeMethod.");
                }

                public Object getResult() {
                    throw new InterceptException("Not support InterceptContext.getResult in beforeMethod.");
                }
            });
        }
        catch (Exception e) {
            throw new ResolverException("Failed to Invoke beforeMethod on Component " + methodResolveContext.getComponent(), e);
        }
    }

    public void resolveMethodAnnotationAfterMethod(final MethodResolveContext methodResolveContext) throws ResolverException {
        Object component = methodResolveContext.getComponent();
        Method method = methodResolveContext.getMethod();
        if(!(component instanceof Interceptable)) {
            throw new ResolverException("Component " + methodResolveContext.getComponent() + " has @Intercept on Method: " + method.getName() + ", but doesn't implement " + Interceptable.class.getName());
        }

        try {
            ((Interceptable) component).afterMethod(new InterceptContext() {

                public Object getTarget() {
                    return methodResolveContext.getComponent();
                }

                public Method getMethod() {
                    return methodResolveContext.getMethod();
                }

                public Object[] getParameters() {
                    return methodResolveContext.getParameters();
                }

                public void setParameters(Object[] objects) {
                    methodResolveContext.setParameters(objects);
                }

                public void setResult(Object result) {
                    methodResolveContext.setResult(result);
                }

                public Object getResult() {
                    return methodResolveContext.getResult();
                }
            });
        }
        catch (Exception e) {
            throw new ResolverException("Failed to Invoke beforeMethod on Component " + methodResolveContext.getComponent(), e);
        }
    }
}
